#pragma once

#include "base.h"
#include <stdio.h>
#include <stdlib.h>

#define LOG_TRACE 6
#define LOG_VERBOSE 5
#define LOG_DEBUG 4
#define LOG_INFO 3
#define LOG_WARN 2
#define LOG_ERROR 1

#ifndef LOG_LEVEL
#define LOG_LEVEL 6
#endif

#define _ANSI_DISABLE "\033[0m"
#define _ANSI_BOLD "\033[1m"
#define _ANSI_GRAY "\033[30m"
#define _ANSI_RED "\033[31m"
#define _ANSI_GREEN "\033[32m"
#define _ANSI_YELLOW "\033[33m"
#define _ANSI_BLUE "\033[34m"
#define _ANSI_PURPLE "\033[35m"
#define _ANSI_CYAN "\033[36m"
#define _ANSI_WHITE "\033[37m"
#define _ANSI_NORMAL "\033[38m"


#define _LOGCAT(level, effect, fmt, ...) do { \
  fprintf(stderr, effect fmt "\n" _ANSI_DISABLE, ##__VA_ARGS__); \
} while(0)

#define _ASSERT(expr, fmt, ...) do { \
  fprintf(stderr, _ANSI_DISABLE _ANSI_BOLD _ANSI_RED "%s:%d: \"" expr "\" assertion failed:\n", __FILE__, __LINE__); \
  fprintf(stderr, fmt "\n" _ANSI_DISABLE, ##__VA_ARGS__); \
  abort(); \
} while(0)

#if LOG_LEVEL >= LOG_ERROR
#define log_e(fmt, ...) _LOGCAT(LOG_ERROR, _ANSI_DISABLE _ANSI_BOLD _ANSI_RED, fmt, ##__VA_ARGS__)
#else
#define log_e(fmt, ...)
#endif

#if LOG_LEVEL >= LOG_WARN
#define log_w(fmt, ...) _LOGCAT(LOG_WARN, _ANSI_DISABLE _ANSI_BOLD _ANSI_YELLOW, fmt, ##__VA_ARGS__)
#else
#define log_w(fmt, ...)
#endif

#if LOG_LEVEL >= LOG_INFO
#define log_i(fmt, ...) _LOGCAT(LOG_INFO, _ANSI_DISABLE _ANSI_BOLD _ANSI_GREEN, fmt, ##__VA_ARGS__)
#else
#define log_i(fmt, ...)
#endif

#if LOG_LEVEL >= LOG_DEBUG
#define log_d(fmt, ...) _LOGCAT(LOG_DEBUG, _ANSI_DISABLE _ANSI_BOLD _ANSI_BLUE, fmt, ##__VA_ARGS__)
#else
#define log_d(fmt, ...)
#endif

#if LOG_LEVEL >= LOG_VERBOSE
#define log_v(fmt, ...) _LOGCAT(LOG_VERBOSE, _ANSI_DISABLE _ANSI_WHITE, fmt, ##__VA_ARGS__)
#else
#define log_v(fmt, ...)
#endif

#if LOG_LEVEL >= LOG_TRACE
#define log_t(fmt, ...) _LOGCAT(LOG_TRACE, _ANSI_DISABLE _ANSI_WHITE, "%s: " fmt, __PRETTY_FUNCTION__, ##__VA_ARGS__)
#else
#define log_t(fmt, ...)
#endif

#if NDEBUG
#define debug_assert(expr, ...)
#else
#define debug_assert(expr, ...) do { \
  if (UNLIKE(!bool(expr))) \
    _ASSERT(#expr, "" __VA_ARGS__); \
} while(0)
#endif

#define abort_assert(expr, ...) do { \
  if (UNLIKE(!bool(expr))) \
    _ASSERT(#expr, "" __VA_ARGS__); \
} while(0)
